| Types > Namespaces > CustomAttributes |
|
In Delphi for Microsoftâ .NET, a unit is still the basic container for types.
Microsoft's Common Language Runtime introduces another layer of organization
called a namespace. In the .NET Framework, a namespace is a conceptual container
of types In Delphi for .NET, a namespace is a container of Delphi units. The
addition of namespaces gives Delphi the ability to access and extend classes in
the .NET Framework.
Unlike traditional Delphi
units, namespaces can be nested to form a containment hierarchy. Nested
namespaces provide a way to organize identifiers and types, and are used to
disambiguate types with the same name. Since they are a container for Delphi
units, namespaces may also be used to differentiate between units of the same name,
that reside in different packages.
For example, the class
MyClass in MyNameSpace, is different from the class MyClass in YourNamespace.
At runtime, the CLR always refers to classes and types by their fully qualified
names: the assembly name, followed by the namespace that contains the type. The
CLR itself has no concept or implementation of the namespace hierarchy; it is
purely a notational convenience of the programming language.
In Delphi for .NET, a
project file (program, library, or package) implicitly introduces its own
namespace, called the project default namespace. A unit may be a member of the
project default namespace, or it may explicitly declare itself to be a member
of a different namespace. In either case, a unit declares its namespace
membership in its Unit statement. For example, consider the following explicit
namespace declaration:
Unit MyCompany.MyWidgets.MyUnit;
First, notice that
namespaces are separated by dots. Namespaces do not introduce new symbols for
the identifiers between the dots; the dots are actually part of the unit name.
The source file name for this example is MyCompany.MyWidgets.MyUnit.pas, and
the DCU output file name is MyCompany.MyWidgets.MyUnit.dcu.
Second, notice that the dots
imply the conceptual nesting, or containment, of one namespace within another.
The example above declares the unit MyUnit to be a member of the MyWidgets
namespace, which itself is contained in the MyCompany namespace. Again, it
should be noted that this containment is for documentation purposes only.
A project default namespace
declares a namespace for all of the units in the project. Consider the
following declarations:
Program MyCompany.MyDepartment.MyProgram;
Library
MyCompany.MyDepartment.MyLibs.MyLibrary;
Package
MyCompany.MyDepartment.MyPackages.MyPackage;
These statements establish
the project default namespace for the program, library, and package,
respectively. The namespace is determined by removing the rightmost identifier
(and dot) from the declaration.
A unit that omits an
explicit namespace is called generic unit. A generic unit automatically
becomes a member of the project default namespace. Given the preceding Program
declaration, the following unit declaration would cause the compiler to treat
MyUnit as a member of the MyCompany.MyDepartment namespace.
Unit MyUnit;
The project default
namespace does not affect the name of the Delphi source file for a generic
unit. In the preceding example, the Delphi source file name would be
MyUnit.pas. The compiler does however prefix the DCU file name with the project
default namespace. The resulting DCU file in the current example would be
MyCompany.MyDepartment.MyUnit.dcu.
Namespace strings are not
case-sensitive. The compiler considers two namespaces that differ only in case
to be equivalent. However, the compiler does preserve the case of a namespace,
and will use the preserved casing in output file names, error messages, and
RTTI unit identifiers. RTTI for class and type names will include the full namespace
specification.
A unit in Delphi for .NET
must still declare the other units on which it depends. The compiler must
search these units for identifiers, the same as always. For units in explicit
namespaces the search scope is already known, but for generic units, the
compiler must establish a namespace search scope.
Consider the following Unit
and Uses declarations:
Unit
MyCompany.MyDepartment.MyUnits.MyUnit1;
Uses MyCompany.OtherDepartment.TheirUnit,
UnitX, UnitY;
These declarations establish
MyUnit1 as a member of the MyCompany.MyDepartment.MyUnits namespace. MyUnit1
depends on three other units, MyCompany.OtherDepartment.TheirUnit, UnitX, and
UnitY. The compiler can easily resolve identifier names in TheirUnit, since the
Uses clause tells it exactly where to look. But how should the compiler resolve
identifier names in UnitX and UnitY?
Search locations can come
from four possible sources: Compiler options, the project file's Namespaces
clause, the project default namespace, and finally, the current unit's
namespace.
A project file (program,
library or package) may optionally specify a list of namespaces to be searched
when resolving generic unit names. The Namespaces clause must appear in the
project file, immediately after the Program, Library, or Package declaration
and before any other clause or block type. The Namespaces clause is a list of
namespace identifiers, separated by commas. A semicolon must terminate the list
of namespaces. The following example establishes a Program and namespace search
list:
Program MyCompany.MyDepartment.MyProgram;
Namespaces
MyCompany.OtherDepartment.TheirWidgets, MyCompany.MyOtherNamespace;
The compiler resolves
identifier names in the following order:
Continuing with the example
above, the compiler would use the following search scope when resolving
identifier names in UnitX and UnitY:
Note that if the current
unit is generic (i.e. it does not have an explicit namespace declaration in its
Unit statement), then resolution begins with the project default namespace.
Delphi's Uses clause brings
a module into the context of the current unit. The Uses clause must either
refer to a module by its fully qualified name (i.e. including the full
namespaces specification), or by its generic name, thereby relying on the
namespace resolution mechanisms to locate the unit. The following example
demonstrates the Uses clause with namespaces:
Unit
MyCompany.MyDepartment.MyUnits.MyUnit1
Uses MyCompany.MyDepartment.OtherUnit, // Fully qualified name.
UnitX; // Generic name.
Once a module has been
brought into context, source code can refer to identifiers within that module
either by the unqualified name, or by the fully qualified name (if necessary,
to disambiguate identifiers with the same name in different units). The
following writeln statements are equivalent:
Uses MyCompany.MyDepartment.OtherUnit;
begin
writeln(MyCompany.MyDepartment.OtherUnit.SomeString);
writeln(SomeString);
end.
A fully qualified identifier
must include the full namespace specification. In the preceding example, it
would be an error to refer to SomeString using only a portion of the namespace:
writeln(OtherUnit.SomeString); // ERROR!
writeln(MyDepartment.OtherUnit.SomeString); //
ERROR!
writeln(MyCompany.MyDepartment.OtherUnit.SomeString); // Correct.
writeln(SomeString); //
Correct.
It is also an error to refer
to only a portion of a namespace in the Uses clause. There is no mechanism to
import all units and symbols in a namespace. The following code does not import
all symbols in the MyCompany namespace:
Uses MyCompany; // ERROR!
This restriction also
applies to the with-do statement. The following will produce a compiler error:
with MyCompany.MyDepartment do // ERROR!
&begin('a string');
Namespaces can become quite
long and cumbersome to type. A new feature of the Delphi language allows you to
introduce an alias for a longwinded namespace. The following Uses clause
introduces a local unit alias:
Uses MyCompany.MyDepartment.OtherUnit as
aUnit;
Given a unit alias, your
source code can refer to an identifier by the shorter name:
// These two statements are equivalent.
writeln(aUnit.SomeString);
writeln(MyCompany.MyDepartment.OtherUnit.SomeString);
Unit aliases must not
conflict with other unit or namespace identifiers. Unit aliases introduce
Delphi identifiers, and therefore cannot contain dots. A unit alias is local to
the unit in which it is declared. A unit alias does not obscure the fully
qualified unit name that it aliases; you may always refer to an identifier
either by it's fully qualified name, or by a unit alias.
| Top | Contents | Copyrights | Borland Developer Network | Borland Home |